package me.w70.bot.methods;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.regex.Pattern;

import me.w70.bot.Bot;
import me.w70.bot.gui.rs.Keyboard;
import me.w70.bot.gui.rs.Mouse;
import me.w70.bot.util.Maths;
import me.w70.bot.wrappers.RSCharacter;
import me.w70.bot.wrappers.RSInterface;
import me.w70.bot.wrappers.RSInterfaceGroup;
import me.w70.bot.wrappers.RSInterfaces;
import me.w70.bot.wrappers.RSItem;
import me.w70.bot.wrappers.RSItemTile;
import me.w70.bot.wrappers.RSNPC;
import me.w70.bot.wrappers.RSObject;
import me.w70.bot.wrappers.RSPlayer;
import me.w70.hooks.Client;
import me.w70.hooks.GroundObject;
import me.w70.hooks.GroundTile;
import me.w70.hooks.ItemNode;
import me.w70.hooks.NPC;
import me.w70.hooks.Node;
import me.w70.hooks.NodeList;
import me.w70.hooks.PhysicalObject;
import me.w70.hooks.Player;
import me.w70.hooks.Tile;

public class Methods extends Thread implements Constants {

	/* Some methods are credit to RSBot\inspired by RSBOT
	 * Thanks to that Team
	 */
	public enum CHAT_MODE {
		VIEW, ON, FRIENDS, OFF, HIDE
	}

	private static Pattern stripFormatting = Pattern.compile("\\<.+?\\>");

	public static String statsNameArray[] = { "attack", "defence", "strength",
			"hitpoints", "ranged", "prayer", "magic", "cooking", "woodcutting",
			"fletching", "fishing", "firemaking", "crafting", "smithing",
			"mining", "herblore", "agility", "thieving", "slayer", "farming",
			"runecraft", "construction", "hunter", "summoning" };

	public static int distanceBetween(Tile t1, Tile t2) {

		return (int) Math.hypot(t2.getX() - t1.getX(), t2.getY() - t1.getY());
	}
	
	

	public boolean dropAllExcept(final int... items) { 
		int inventoryCount = getInventoryCount();
		RSItem[] inventoryArray = getItems();
		outer: for (int off = 0; off < inventoryArray.length; off++) {
			if (inventoryArray[off].getID() == -1) {
				continue;
			}
			for (final int item : items) {
				if (inventoryArray[off].getID() == item) {
					continue outer;
				}
			}

			for (int tries = 0; tries < 5; tries++) {
				atInventoryItem(inventoryArray[off].getID(), "Drop");
				wait(random(500, 700));
				if (getInventoryCount() < inventoryCount) {
					break;
				}
			}
			if (getInventoryCount() >= inventoryCount) {
	
				return false;
			}
			inventoryArray = getItems();
			inventoryCount = getInventoryCount();
		}
		return true;
	}
	
	public boolean dropItems(final int... items) { 
		int inventoryCount = getInventoryCount();
		RSItem[] inventoryArray = getItems();
		outer: for (int off = 0; off < inventoryArray.length; off++) {
			if (inventoryArray[off].getID() == -1) {
				continue;
			}
			boolean found = false;
			for (final int item : items) {
				if (inventoryArray[off].getID() == item) {
					found = true;
				}
			}
			
			if(!found) {
				continue;
			}

			for (int tries = 0; tries < 5; tries++) {
				atInventoryItem(inventoryArray[off].getID(), "Drop");
				wait(random(500, 700));
				if (getInventoryCount() < inventoryCount) {
					break;
				}
			}
			if (getInventoryCount() >= inventoryCount) {
	
				return false;
			}
			inventoryArray = getItems();
			inventoryCount = getInventoryCount();
		}
		return true;
	}
	
	public static void reverse(Object[] o) {
		int left;
		int right;
		for (left = 0, right = o.length - 1; left < right; left++, right--) {
			// exchange the first and last
			Object temp = o[left];
			o[left] = o[right];
			o[right] = temp;
		}

	}

	public static Tile[] reversePath(Tile[] in) {
		Tile[] out = (Tile[]) in.clone();
		reverse(out);
		return (Tile[]) out;
	}

	/* STRING UTIL */
	public static String stripFomatting(final String input) {
		return Methods.stripFormatting.matcher(input).replaceAll("");
	}

	ActiveMove m = new ActiveMove();

	TyperQueue typerQueue;

	Typer typer;

	Random random = new Random();

	public Methods() {
		// Create the work queue
		typerQueue = new TyperQueue();

		typer = new Typer(typerQueue);

		typer.start();

	}
	
	

	public boolean arrayContains(Object[] array, Object match) {
		for (Object s : array) {
			if (s.equals(match)) {
				return true;
			}
		}
		return false;
	}

	public boolean atDoor(final RSObject object, final String direction,
			final String action) {
		if (object == null) {
			return false;
		}
		if (getClient().isMenuOpen()) {
			atMenu("Cancel");
		}
		final Tile objectPos = object.getLocation();
		Point t = null;
		if (direction.toLowerCase().startsWith("n")) {
			t = Calc.tileToScreen(objectPos.getX(), objectPos.getY() + 1, .5,
					.1, 40);
		} else if (direction.toLowerCase().startsWith("s")) {
			t = Calc.tileToScreen(objectPos.getX(), objectPos.getY(), .5, .1,
					40);
		} else if (direction.toLowerCase().startsWith("w")) {
			t = Calc.tileToScreen(objectPos.getX(), objectPos.getY(), .1, .5,
					40);
		} else if (direction.toLowerCase().startsWith("e")) {
			t = Calc.tileToScreen(objectPos.getX() + 1, objectPos.getY(), .1,
					.5, 40);
		}
		if (t == null) {
			return false;
		}
		clickMouse(t, 3, 3, false);
		return atMenu(action);
	}

	public boolean atInterface(final int iface, final int child) {
		return atInterface(getInterface(iface, child));
	}

	public boolean atInterface(final int iface, final int child,
			final String actionContains) {
		return atInterface(getInterface(iface, child), actionContains);
	}

	public boolean atInterface(final RSInterface i) {
		if (!i.isValid()) {
			return false;
		}
		final Rectangle pos = i.getArea();
		if (pos.x == -1 || pos.y == -1 || pos.width == -1 || pos.height == -1) {
			return false;
		}
		clickMouse((int) random(pos.getMinX(), pos.getMaxX() - 10),
				(int) random(pos.getMinY(), pos.getMaxY()), true);
		return true;
	}

	public boolean atInterface(final RSInterface i, final String actionContains) {
		if (!i.isValid()) {
			return false;
		}
		final Rectangle pos = i.getArea();
		if (pos.x == -1 || pos.y == -1 || pos.width == -1 || pos.height == -1) {
			return false;
		}
		moveMouse((int) random(pos.getMinX(), pos.getMaxX() - 10),
				(int) random(pos.getMinY(), pos.getMaxY()));
		if (getMenuItems().get(0).toLowerCase().contains(
				actionContains.toLowerCase())) {
			clickMouse(true);
		} else {
			clickMouse(false);
			if (!atMenu(actionContains)) {
				return false;
			}
		}
		return true;
	}

	public boolean atInventoryItem(final int itemID, final String option) {
		if (getCurrentTab() != Constants.TAB_INVENTORY
				&& !RSInterfaces.getInterface(BANK_INTERFACEGROUP_ID).isValid()
				&& !RSInterfaces.getInterface(Constants.INTERFACE_STORE)
						.isValid()) {
			openTab(Constants.TAB_INVENTORY);
		}
		final RSItem[] items = getItems();
		final java.util.List<Integer> possible = new ArrayList<Integer>();
		for (int i = 0; i < items.length; i++) {
			if (items[i].getID() == itemID) {
				possible.add(i);
			}
		}
		if (possible.size() == 0) {
			return false;
		}
		final int idx = possible.get(random(0, possible.size()));
		final Point t = getInventoryItemPoint(idx);
		clickMouse(t, 5, 5, false);
		return atMenu(option);
	}

	public boolean atMenu(final String optionContains) {
		int idx = getMenuIndex(optionContains);
		// log.info((optionContains + " " + idx + " " + getMenuItems());
		if (!isMenuOpen()) {
			if (idx == -1) {
				return false;
			}
			if (idx == 0) {
				clickMouse(true);
			} else {
				clickMouse(false);
				atMenuItem(idx);
			}
			return true;
		} else {
			if (idx == -1) {
				idx = getMenuIndex("Cancel");
				atMenuItem(idx);
				return false;
			} else {
				atMenuItem(idx);
				return true;
			}
		}
	}

	public boolean atMenu(final String[] items) {
		for (final String target : getMenuItems()) {
			if (arrayContains(items, target)) {
				return atMenu(target);
			}
		}
		atMenu("Cancel");
		return false;
	}

	public boolean atMenuItem(final int i) {
		if (!isMenuOpen()) {
			return false;
		}
		try {
			final Tile menu = getMenuLocation();
			final int xOff = random(4, getMenuItems().get(i).length() * 4);
			final int yOff = random(21, 29) + 15 * i;
			clickMouse(menu.getX() + xOff, menu.getY() + yOff, 2, 2, true);
			return true;
		} catch (final Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public boolean atNPC(final RSNPC npc, final String action) {
		if (npc == null) {
			return false;
		}
		final Tile tile = npc.getLocation();
		if (!tile.isValid()) {
			return false;
		}
		if (distanceTo(tile) > 5) {
			walkTile(tile);
		}
		return clickCharacter(npc, action);
	}

	public boolean atObject(final RSObject object, final String action) {
		return atTile(object.getLocation(), action);
	}

	public boolean atTile(final Tile tile, final int h, final double xd,
			final double yd, final String action) {
		try {
			final Point location = Calc.tileToScreen(tile.getX(), tile.getY(),
					.5, .5, h);
			if (location.x == -1 || location.y == -1) {
				return false;
			}
			moveMouse(location, 3, 3);
			if (getMenuItems().get(0).toLowerCase().contains(
					action.toLowerCase())) {
				clickMouse(true);
			} else {
				clickMouse(false);
				if (!atMenu(action)) {
					return false;
				}
			}
			wait(random(500, 1000));
			while (true) {
				if (!getMyPlayer().isMoving()) {
					break;
				}
				wait(random(500, 1000));
			}
			return true;
		} catch (final Exception e) {
			return false;
		}
	}

	public boolean atTile(final Tile tile, final String action) {
		try {
			final Point location = Calc.tileToScreen(tile);
			if (location.x == -1 || location.y == -1) {
				return false;
			}
			moveMouse(location, 3, 3);
			return atMenu(action);

		} catch (final Exception e) {
			return false;
		}
	}

	public boolean bankContains(int itemID) {
		if (!isBankOpen())
			return false;
		RSInterface itemPane = getBankInterfaceGroup().getInterface(
				BANK_ITEMPANE);
		for (RSInterface item : itemPane.getChildren()) {
			if (item == null) {
				return false; //
			}
			if (item.getContainedItemID() == itemID) {
				return true;
			}
		}
		return false;
	}

	public boolean bankContains(String itemName) {
		if (!isBankOpen())
			return false;
		RSInterface itemPane = getBankInterfaceGroup().getInterface(
				BANK_ITEMPANE);
		for (RSInterface item : itemPane.getChildren()) {
			if (item == null) {
				return false; //
			}
			if (item.getContainedItemName().equalsIgnoreCase(itemName)) {
				return true;
			}
		}
		return false;
	}

	public boolean canContinue() {
		return getContinueInterface() != null;
	}

	public boolean clickCharacter(final RSCharacter c, final String action) {
		try {
			Point screenLoc = null;
			for (int i = 0; i < 20; i++) {
				screenLoc = c.getScreenLocation();
				if (!c.isValid() || !Calc.onScreen(screenLoc)) {
					Bot.addMessage(this, "Not on screen " + action);
					return false;
				}
				if (getMouseLocation().equals(screenLoc)) {
					break;
				}
				moveMouse(screenLoc);
			}
			screenLoc = c.getScreenLocation();
			if (!getMouseLocation().equals(screenLoc)) {
				return false;
			}
			final List<String> items = getMenuItems();
			if (items.size() <= 1) {
				return false;
			}
			if (items.get(0).toLowerCase().contains(action.toLowerCase())) {
				clickMouse(screenLoc, true);
				return true;
			} else {
				clickMouse(screenLoc, false);
				return atMenu(action);
			}
		} catch (final Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public boolean clickContinue() {
		final RSInterface cont = getContinueChildInterface();
		if (cont != null && cont.isValid()) {
			return atInterface(cont);
		}
		return true;
	}

	public boolean clickMouse() {
		return clickMouse(true);
	}

	public boolean clickMouse(boolean left) {
		return Bot.gui.rs.mouseManager.clickMouse(left);
	}

	public boolean clickMouse(int x, int y, boolean left) {
		if (!moveMouse(x, y))
			return false;
		return clickMouse(left);
	}

	public boolean clickMouse(int x, int y, int randXDeviation,
			int randYDeviation, boolean left) {
		if (!moveMouse(x, y, randXDeviation, randYDeviation))
			return false;
		return clickMouse(left);
	}

	public boolean clickMouse(Point p, boolean left) {
		if (!moveMouse(p.x, p.y))
			return false;
		return clickMouse(left);
	}

	public boolean clickMouse(Point p, int randXDeviation, int randYDeviation,
			boolean left) {
		if (!moveMouse(p.x, p.y, randXDeviation, randYDeviation))
			return false;
		return clickMouse(left);
	}

	public void clickObject(RSObject o, int xdev, int ydev) {
		clickMouse(o.getScreenPos(), xdev, ydev, true);
	}

	public void closeBank() {
		if (isBankOpen()) {
			getBankInterfaceGroup().getInterface(BANK_BUTTON_CLOSE).doClick();
		}
	}

	public void deposit(int itemID, int count) {
		java.util.List<Integer> dropPositions = new LinkedList<Integer>();
		RSItem[] inventoryArray = getItems();
		for (int iterator1 = 0; iterator1 < inventoryArray.length; iterator1++) {
			if (inventoryArray[iterator1].isValid()) {
				if (inventoryArray[iterator1].getID() == itemID)
					dropPositions.add(iterator1);
			}
		}
		if (dropPositions.size() == 0)
			return;
		int[] ascending = new int[dropPositions.size()];
		int index = 0;
		for (int pos : dropPositions) {
			ascending[index] = pos;
			index++;
		}
		Arrays.sort(ascending);
		// Random drop position
		int randomIndex = random(0, ascending.length - 1);
		int pos = ascending[randomIndex];
		if (isMenuOpen()) {
			atMenu("cancel");
		}
		Point p = getInventoryItemPoint(pos);
		moveMouse(p);
		wait(random(20, 100));
		if (count == MENU_ALL) {
			atMenu("Deposit-All");
		} else if (count == MENU_ALL_BUT_ONE) {
			atMenu("Deposit-All but one");
		} else {
			if (!atMenu("Deposit-" + count)) {
				atMenu("Deposit-X");
				for (int i = 0; i < 21; i++) {
					if (RSInterfaces.interfaceGroupExists(752)) {
						sendText("" + count, true);
					}
					wait(random(80, 130));
				}
			}
		}
		atMenu("drop");
		wait(random(150, 300));
	}

	public void deposit(int count, int... itemIDs) {
		if (itemIDs == null)
			return;
		for (int id : itemIDs) {
			deposit(id, count);
		}
	}
	
	public boolean depositAllExcept(final int... items) {
		int inventoryCount = getInventoryCount();
		RSItem[] inventoryArray = getItems();
		outer: for (int off = 0; off < inventoryArray.length; off++) {
			if (inventoryArray[off].getID() == -1) {
				continue;
			}
			for (final int item : items) {
				if (inventoryArray[off].getID() == item) {
					continue outer;
				}
			}

			for (int tries = 0; tries < 5; tries++) {
				atInventoryItem(inventoryArray[off].getID(), "Deposit-All");
				wait(random(500, 700));
				if (getInventoryCount() < inventoryCount) {
					break;
				}
			}
			if (getInventoryCount() >= inventoryCount) {
	
				return false;
			}
			inventoryArray = getItems();
			inventoryCount = getInventoryCount();
		}
		return true;
	}
	
	public boolean isInventoryFull() {
		return getInventoryCount() == 28;
	}
	
	private static Tile current = new Tile(0, 0);
	public boolean walkPath(final Tile[] path, final int maxDist) {
		try {
			final Tile next = nextTile(path, maxDist);
			if (next != null && !next.equals(Methods.current)) {
				walkTile(next);
				Methods.current = next;
				return false;
			} else if (next != null && next.equals(Methods.current)) {
				return false;
			}
		} catch (final Exception e) {
			return false;
		}
		return true;
	}

	

	public Tile nextTile(final Tile[] path, final int maxDist) {
		final Tile cur = getMyPlayer().getLocation();
		for (int i = path.length - 1; i >= 0; i--) {
			if (Methods.distanceBetween(cur, path[i]) <= maxDist
					&& Methods.distanceBetween(cur, path[path.length - 1]) > 5) {
				return path[i];
			}
		}
		return null;
	}

	public void depositAll(int... itemIDs) {
		if (itemIDs == null)
			return;
		for (int id : itemIDs) {
			deposit(id, MENU_ALL);
		}
	}

	public boolean depositBeastsBurder() {
		if (!isBankOpen())
			return false;
		getBankInterfaceGroup().getInterface(BANK_BUTTON_DEPOSIT_BEASTS_BURDEN)
				.doClick();
		return true;
	}

	public boolean depositInventory() {
		if (!isBankOpen())
			return false;
		getBankInterfaceGroup().getInterface(BANK_BUTTON_DEPOSIT_CARRIED_ITEMS)
				.doClick();
		return true;
	}

	public boolean depositWornItems() {
		if (!isBankOpen())
			return false;
		getBankInterfaceGroup().getInterface(BANK_BUTTON_DEPOSIT_WORN_ITEMS)
				.doClick();
		return true;
	}

	public int distanceTo(RSCharacter c) {
		return c == null ? Integer.MAX_VALUE : distanceTo(c.getLocation());
	}

	public int distanceTo(Tile t) {

		return t == null ? Integer.MAX_VALUE : distanceBetween(getMyLocation(),
				t);
	}

	public boolean dragInterfaces(final RSInterface int1, final RSInterface int2) {
		final Point start = returnMidInterface(int1);
		final Point finish = returnMidInterface(int2);

		moveMouse(start);
		dragMouse(finish);
		return true;
	}

	public void dragMouse(int x, int y) {
		Bot.gui.rs.mouseManager.dragMouse(x, y);
	}

	public void dragMouse(Point p) {
		dragMouse(p.x, p.y);
	}

	public boolean equipmentContains(final int... itemID) {
		final RSInterface equip = getEquipmentInterface();
		int count = 0;
		for (final int item : itemID) {
			if (getInventoryItemCount(item) != 0) {
				count++;
			}
		}
		return count == itemID.length;
	}

	public RSObject findObject(final int... ids) {
		// Changed to find the nearest, reachable!
		// fixed, lol, getObjectAt want a real xy not this one
		RSObject cur = null;
		int dist = -1;
		for (int x = 0; x < 104; x++) {
			for (int y = 0; y < 104; y++) {
				final RSObject o = getObjectAt(x + getClient().getBaseX(), y
						+ getClient().getBaseY());
				if (o != null) {
					boolean isObject = false;
					for (final int id : ids) {
						if (o.getID() == id) {
							isObject = true;
							break;
						}
					}
					if (isObject) {
						final int distTmp = getRealDistanceTo(o.getLocation(),
								true);
						if (distTmp != -1) {
							if (cur == null) {
								dist = distTmp;
								cur = o;
							} else if (distTmp < dist) {
								cur = o;
								dist = distTmp;
							}
						}
					}
				}
			}
		}
		return cur;
	}

	public RSObject findObject(final int range, final int id) {
		final int minX = getMyPlayer().getLocation().getX() - range;
		final int minY = getMyPlayer().getLocation().getY() - range;
		final int maxX = getMyPlayer().getLocation().getX() + range;
		final int maxY = getMyPlayer().getLocation().getY() + range;
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				final RSObject o = getObjectAt(x, y);
				if (o != null) {
					if (o.getID() == id) {
						return o;
					}
				}
			}
		}
		return null;
	}

	public RSObject findObject(final int range, final int[] ids) {
		final int minX = getMyPlayer().getLocation().getX() - range;
		final int minY = getMyPlayer().getLocation().getY() - range;
		final int maxX = getMyPlayer().getLocation().getX() + range;
		final int maxY = getMyPlayer().getLocation().getY() + range;
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				final RSObject o = getObjectAt(x, y);
				if (o != null) {
					for (final int id : ids) {
						if (o.getID() == id) {
							return o;
						}
					}
				}
			}
		}
		return null;
	}

	public RSInterfaceGroup getBankInterfaceGroup() {
		return RSInterfaces.getInterfaceGroup(BANK_INTERFACEGROUP_ID);
	}

	public RSInterface getBankItem(int itemID) {
		if (!isBankOpen())
			return null;
		RSInterface itemPane = getBankInterfaceGroup().getInterface(
				BANK_ITEMPANE);
		for (RSInterface item : itemPane.getChildren()) {
			if (item == null) {
				return null;
			}
			if (item.getContainedItemID() == itemID) {
				return item;
			}
		}
		return null;
	}

	public RSInterface getBankItem(String itemName) {
		if (!isBankOpen())
			return null;
		RSInterface itemPane = getBankInterfaceGroup().getInterface(
				BANK_ITEMPANE);
		for (RSInterface item : itemPane.getChildren()) {
			if (item == null) {
				return null;
			}
			if (item.getContainedItemName().equalsIgnoreCase(itemName)) {
				return item;
			}
		}
		return null;
	}

	public int getCameraAngle() {
		double mapAngle = getClient().getCameraCurveX();
		mapAngle /= 2048;
		mapAngle *= 360;
		return (int) mapAngle;
	}

	/* CAMERA */
	public int getCameraAngleTo(Tile tile) {
		RSPlayer myPlayer = getMyPlayer();
		int x1 = myPlayer.getLocation().getX();
		int y1 = getMyPlayer().getLocation().getY();
		int x = x1 - tile.getX();
		int y = y1 - tile.getY();
		double angle = Math.toDegrees(Math.atan2(x, y));
		if (x == 0 && y > 0)
			angle = 180;
		if (x < 0 && y == 0)
			angle = 90;
		if (x == 0 && y < 0)
			angle = 0;
		if (x < 0 && y == 0)
			angle = 270;
		if (x < 0 && y > 0)
			angle += 270;
		if (x > 0 && y > 0)
			angle += 90;
		if (x < 0 && y < 0)
			angle = Math.abs(angle) - 180;
		if (x > 0 && y < 0)
			angle = Math.abs(angle) + 270;
		if (angle < 0)
			angle = 360 + angle;
		if (angle >= 360)
			angle -= 360;
		return (int) angle;
	}

	public Client getClient() {
		Client c;
		try {
			c = (Client) Bot.gui.rs.getClient();
		} catch (NullPointerException e) {
			return null;
		}
		return c;
	}


	public RSObject getClosestObject(int range, int... ids) {
		Tile myPos = getMyPlayer().getLocation();
		int minX = myPos.getX() - range;
		int minY = getMyPlayer().getLocation().getY() - range;
		int maxX = getMyPlayer().getLocation().getX() + range;
		int maxY = getMyPlayer().getLocation().getY() + range;
		List<RSObject> objList = new ArrayList<RSObject>();
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				RSObject o = getObjectAt(x, y);
				if (o != null) {
					for (int id : ids) {
						if (o.getID() == id) {
							objList.add(o);
						}
					}
				}
			}
		}
		if (objList.isEmpty())
			return null;
		int closest = 9999;
		RSObject closestObj = null;
		for (RSObject o : objList) {
			int distance = myPos.distanceTo(o.getLocation());
			if (distance < closest) {
				closest = distance;
				closestObj = o;
			}
		}
		return closestObj;
	}

	public RSInterface getContinueChildInterface() {
		if (getClient().getInterfaces() == null) {
			return null;
		}
		final RSInterface[] valid = RSInterfaces.getAllInterfaces();
		for (final RSInterface iface : valid) {
			final int len = iface.getChildren().length;
			for (int i = 0; i < len; i++) {
				final RSInterface child = iface.getChild(i);
				if (child.containsText("Click here to continue")) {
					// || child.containsAction("Click here to continue")) {
					return child;
				}
			}
		}
		return null;
	}

	public RSInterface getContinueInterface() {
		if (getClient().getInterfaces() == null) {
			return null;
		}
		final RSInterface[] valid = RSInterfaces.getAllInterfaces();
		for (final RSInterface iface : valid) {
			if (iface.containsText("Click here to continue")) {
				// || iface.containsAction("Click here to continue")) {
				return iface;
			}
		}
		return null;
	}

	public int getCurrentTab() {
		RSInterface[] tabs = getTabButtons();

		for (int i = 0; i < tabs.length; i++) {
			if (i == 14 && tabs[i].getTextureID() == 1203)
				return 14; // Logout button

			if (tabs[i].getTextureID() != -1)
				return (i < 7) ? (i + 7) : (i - 7);
		}

		return -1; // no selected ones.
	}

	public int getEnergy() {
		try {
			int runLeft = Integer.parseInt(RSInterfaces.getInterface(750, 5)
					.getText());
			return runLeft;
		} catch (final NumberFormatException e) {
			return -1;
		}
	}

	public RSInterface getEquipmentInterface() {
		return RSInterfaces.getInterface(Constants.EQUIPMENT_X,
				Constants.EQUIPMENT_Y);
	}

	/* SKILLS */
	public int getExperience(final int skill) {
		int[] skills = getClient().getExperiences();
		if (skills == null || skills.length == 0 || skill < 0
				|| skill >= skills.length) {
			return -1;
		}
		return skills[skill];
	}

	public int getExperienceToLevel(int skill, int level) {
		if (level < 0 || level > 99)
			return -1;
		int experience = getExperience(skill);
		if (experience == -1)
			return -1;
		return expTable[level] - experience;
	}

	public int getExperienceToNextLevel(int skill) {
		int level = getLevel(skill);
		if (level == -1) {
			return -1;
		}
		if (level == 99)
			return 0;

		int experience = getExperience(skill);
		int needed = expTable[level + 1];
		return needed - experience;
	}

	public int getFightMode() {
		return getSetting(43);
	}

	public int getGameState() {
		Client client = getClient();
		if (client == null)
			return 0;
		return client.getGameState();
	}

	public RSItemTile getGroundItem(final int range) {
		final int pX = getMyPlayer().getLocation().getX();
		final int pY = getMyPlayer().getLocation().getY();
		final int minX = pX - range;
		final int minY = pY - range;
		final int maxX = pX + range;
		final int maxY = pY + range;
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				final RSItemTile[] items = getGroundItemsAt(x, y);
				if (items.length > 0) {
					return items[0];
				}
			}
		}
		return null;
	}

	
	public RSItemTile getGroundItemByID(final int id) {
		return getGroundItemByID(52, new int[] { id });
	}

	public RSItemTile getGroundItemByID(final int range, final int id) {
		return getGroundItemByID(range, new int[] { id });
	}

	/* NPC FINDING */

	public RSItemTile getGroundItemByID(final int range, final int[] ids) {
		final int pX = getMyPlayer().getLocation().getX();
		final int pY = getMyPlayer().getLocation().getY();
		final int minX = pX - range;
		final int minY = pY - range;
		final int maxX = pX + range;
		final int maxY = pY + range;
		for (int x = minX; x <= maxX; x++) {
			for (int y = minY; y <= maxY; y++) {
				final RSItemTile[] items = getGroundItemsAt(x, y);
				for (final RSItemTile item : items) {
					final int iId = item.getItem().getID();
					for (final int id : ids) {
						if (iId == id) {
							return item;
						}
					}
				}
			}
		}
		return null;
	}

	public RSItemTile getGroundItemByID(final int[] ids) {
		return getGroundItemByID(52, ids);
	}

	public RSItemTile[] getGroundItemsAt(final int x, final int y) {
		try {
			if (!isLoggedIn()) {
				return new RSItemTile[0];
			}
			final NodeList nl = getClient().getNodeLists()[getClient()
					.getPlane()][x - getClient().getBaseX()][y
					- getClient().getBaseY()];
			if (nl == null) {
				return new RSItemTile[0];
			}
			final List<RSItemTile> list = new ArrayList<RSItemTile>();
			final Node holder = nl.getHead();
			Node curNode = holder.getNext();
			while (curNode != null && curNode != holder
					&& curNode != nl.getHead()) {
				final GroundObject node = ((ItemNode) curNode).getItem();
				list.add(new RSItemTile(x, y, new RSItem(node.getID(), node
						.getStackSize())));
				curNode = curNode.getNext();
			}
			return list.toArray(new RSItemTile[list.size()]);
		} catch (final ArrayIndexOutOfBoundsException aiobe) {
			// aiobe.printStackTrace();
			// log.info(("p:" + getPlane() + " x:" + x + " bX:" +
			// Bot.getClient().getBaseX()
			// + " y:" + y + " bY:" + Bot.getClient().getBaseY());
			return new RSItemTile[0];
		}
	}

	public RSItemTile[] getGroundItemsAt(final Tile t) {
		return getGroundItemsAt(t.getX(), t.getY());
	}

	public int getHealth() {
		try {
			return Integer
					.parseInt(RSInterfaces.getInterface(748, 6).getText());
		} catch (final NumberFormatException e) {
			return -1;
		}
	}

	/* INVENTORY */
	public RSInterface getInterface(final int in, final int ina) {
		return RSInterfaces.getInterfaceGroup(in).getInterface(ina);
	}

	public RSInterfaceGroup getInterfaceGroup(int ID) {
		return RSInterfaces.getInterfaceGroup(ID);

	}

	public int getInventoryCount() {
		return getInventoryCount(false);
	}

	public int getInventoryCount(final boolean includeStacks) {
		RSItem[] items = getItems();
		int count = 0;
		for (RSItem item : items) {
			if(item.getID() != -1) {
			if (includeStacks) {
				count = count + item.getStackSize();
			} else {
				
				count++;
				}
			}
		}
		return count;

	}

	public RSInterface getInventoryInterface() {
		RSInterface inventoryInterface = RSInterfaces.getInterface(149, 0);
		return inventoryInterface;

	}

	public RSItem getInventoryItemById(final int... ids) {
		final RSItem[] items = getItems();
		for (final RSItem item : items) {
			for (final int id : ids) {
				if (item.getID() == id) {
					return item;
				}
			}
		}
		return null;
	}

	public int getInventoryItemCount(int item) {
		return getInventoryItemCount(item, false);
	}
	


	public int getInventoryItemCount(int... items) {
		int count = 0;
		for(int item : items) {
			count += getInventoryItemCount(item, true);
		}
		return count;
	}
	
	public RSObject getNearestObjectByID(final int... ids) {
		RSObject cur = null;
		double dist = -1;
		for (int x = 0; x < 104; x++) {
			for (int y = 0; y < 104; y++) {
				final RSObject o = getObjectAt(x + getClient().getBaseX(),
						y + getClient().getBaseY());
				if (o != null) {
					boolean isObject = false;
					for (final int id : ids) {
						if (o.getID() == id) {
							isObject = true;
							break;
						}
					}
					if (isObject) {
						final double distTmp = Calc.calculateDistance(getMyPlayer()
								.getLocation(), o.getLocation());
						if (cur == null) {
							dist = distTmp;
							cur = o;
						} else if (distTmp < dist) {
							cur = o;
							dist = distTmp;
						}
					}
				}
			}
		}
		return cur;
	}
	
	public RSObject getSecondNearestObjectByID(final int... ids) {
		RSObject cur = null;
		RSObject sec = null;
		double dist = -1;
		for (int x = 0; x < 104; x++) {
			for (int y = 0; y < 104; y++) {
				final RSObject o = getObjectAt(x + getClient().getBaseX(),
						y + getClient().getBaseY());
				if (o != null) {
					boolean isObject = false;
					for (final int id : ids) {
						if (o.getID() == id) {
							isObject = true;
							break;
						}
					}
					if (isObject) {
						final double distTmp = Calc.calculateDistance(getMyPlayer()
								.getLocation(), o.getLocation());
						if (cur == null) {
							dist = distTmp;
							cur = o;
							sec = o;
						} else if (distTmp < dist) {
							sec = cur;
							cur = o;
							dist = distTmp;
						}
					}
				}
			}
		}
		return sec;
	}
	
	
	public int getInventoryItemCount(int item, boolean countStacks) {
		RSItem[] items = getItems();
	
		int count = 0;
		for (RSItem rsitem : items) {
			if (rsitem.getID() == item) {
				if (countStacks) {
					count = count + rsitem.getStackSize();
				} else {
					count++;
				}
			}
		}
		return count;
	}

	public Point getInventoryItemPoint(final int invIndex) {
		final int col = invIndex % 4;
		final int row = invIndex / 4;
		final int x = 580 + col * 42;
		final int y = 228 + row * 36;

		return new Point(x, y);
	}

	public RSItem[] getItems() {
		RSInterface inventoryInterface = getInterface(149, 0);
		if (inventoryInterface == null)
			return null;

		int[] idArray = inventoryInterface.getItemIDArray();
		int[] stacks = inventoryInterface.getItemStackSizeArray();
		RSItem[] out = new RSItem[idArray.length];
		for (int i = 0; i < idArray.length; i++) {
			out[i] = new RSItem(idArray[i] - 1, stacks[i]);
		}
		return out;
	}

	public Keyboard getKeyboard() {
		return Bot.gui.rs.keyboardManager;
	}

	public int getLevel(final int skill) {
		int[] skills = getClient().getLevels();
		if (skills == null || skills.length == 0 || skill < 0
				|| skill >= skills.length) {
			return -1;
		}
		return skills[skill];
	}

	public int getMenuIndex(String optionContains) {
		optionContains = optionContains.toLowerCase();
		final java.util.List<String> actions = getMenuItems();
		for (int i = 0; i < actions.size(); i++) {
			final String action = actions.get(i);
			if (action.toLowerCase().contains(optionContains)) {
				return i;
			}
		}
		return -1;
	}

	/* OBJECTS */

	public ArrayList<String> getMenuItems() {
		final String[] options = getClient().getMenuOptions();
		final String[] actions = getClient().getMenuActions();
		final int offset = getClient().getMenuOptionsCount();
		final ArrayList<String> output = new ArrayList<String>();
		for (int i = offset - 1; i >= 0; i--) {
			final String option = options[i];
			final String action = actions[i];
			if (option != null && action != null) {
				final String text = Methods.stripFomatting(action) + ' '
						+ Methods.stripFomatting(option);
				output.add(text);
			}
		}
		return output;
	}

	public Tile getMenuLocation() {
		if (!isMenuOpen()) {
			return null;
		}
		int x = getClient().getMenuX();
		int y = getClient().getMenuY();
		x += 4;
		y += 4;
		return new Tile(x, y);
	}

	public Mouse getMouse() {
		return Bot.gui.rs.mouseManager;
	}

	public Point getMouseLocation() {
		return new Point(Bot.gui.rs.mouseManager.botX,
				Bot.gui.rs.mouseManager.botY);
	}

	public Tile getMyLocation() {
		int x = getClient().getBaseX()
				+ (getClient().getMyPlayer().getPosX() >> 7);
		int y = getClient().getBaseY()
				+ (getClient().getMyPlayer().getPosY() >> 7);
		return (new Tile(x, y));
	}

	public RSPlayer getMyPlayer() {
		return new RSPlayer(getClient().getMyPlayer());
	}

	public RSNPC getNearestFreeNPCByID(final int... ids) {
		int Dist = 20;
		RSNPC closest = null;
		final int[] validNPCs = getClient().getNPCIndices();
		final NPC[] npcs = getClient().getNPCs();

		for (final int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			final RSNPC Monster = new RSNPC(npcs[element]);
			try {
				for (final int id : ids) {
					if (id != Monster.getID() || Monster.isInCombat()) {
						continue;
					}
					final int distance = distanceTo(Monster);
					if (distance < Dist) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (final Exception e) {
				// e.printStackTrace(); TODO: Why?
			}
		}
		return closest;
	}

	public RSNPC getNearestFreeNPCByName(final String... names) {
		int Dist = 20;
		RSNPC closest = null;
		final int[] validNPCs = getClient().getNPCIndices();
		final NPC[] npcs = getClient().getNPCs();

		for (final int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			final RSNPC Monster = new RSNPC(npcs[element]);
			try {
				for (final String name : names) {
					if (name == null || !name.equals(Monster.getName())
							|| Monster.isInCombat()) {
						continue;
					}
					final int distance = distanceTo(Monster);
					if (distance < Dist) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (final Exception e) {
				e.printStackTrace();
			}
		}
		return closest;
	}

	public RSNPC getNearestNPCByID(int... ids) {
		int Dist = 20;
		RSNPC closest = null;
		int[] validNPCs = getClient().getNPCIndices();
		NPC[] npcs = getClient().getNPCs();

		for (int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			try {
				RSNPC Monster = new RSNPC(npcs[element]);
				for (int id : ids) {
					if (id != Monster.getID()) {
						continue;
					}

					int distance = distanceTo(Monster);
					// System.out.println(distance);
					if (distance < Dist) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return closest;
	}

	/* CLICKS */

	public RSNPC getNearestNPCByName(String... names) {
		int Dist = 20;
		RSNPC closest = null;
		int[] validNPCs = getClient().getNPCIndices();
		NPC[] npcs = getClient().getNPCs();

		for (int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			try {
				RSNPC Monster = new RSNPC(npcs[element]);
				for (String name : names) {
					if (!name.equals(Monster.getName())) {
						continue;
					}

					int distance = distanceTo(Monster);
					// System.out.println(distance);
					if (distance < Dist) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return closest;
	}

	public Vector<RSNPC> getNPCs() {
		int[] validNPCs = getClient().getNPCIndices();
		NPC[] npcs = getClient().getNPCs();

		Vector<RSNPC> ret = new Vector<RSNPC>();
		for (int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			try {

				RSNPC Monster = new RSNPC(npcs[element]);
				ret.add(Monster);

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return ret;
	}

	public RSObject getObjectAt(int x, int y) {
		if (getClient().getGroundTiles() == null)
			return null;
		for (int i = 0; i < 4; i++) {
			try {
				GroundTile groundTile = getClient().getGroundTiles()[i][x
						- getClient().getBaseX()][y - getClient().getBaseY()];
				if (groundTile != null) {
					try {

						if (groundTile.getInteractivePhysicalObjects() != null) {
							for (PhysicalObject obj : groundTile
									.getInteractivePhysicalObjects()) {
								if (obj != null && obj.getUID() != -1L) {
									RSObject physObject = new RSObject(obj);
									if (physObject.getType() == 2) // We don't
										// want
										// NPC players
										// and such
										return physObject;
								}
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
					if (groundTile.getItemPhysicalObject() != null

					&& groundTile.getItemPhysicalObject().getUID() != -1L) {
						RSObject physObject = new RSObject(groundTile
								.getItemPhysicalObject());
						if (physObject.getType() == 2) // We don't want NPCs
							// players and such
							return physObject;
					}
					if (groundTile.getPhysicalObject3() != null
							&& groundTile.getPhysicalObject3().getUID() != -1L) {
						RSObject physObject = new RSObject(groundTile
								.getPhysicalObject2());
						if (physObject.getType() == 2) // We don't want NPCs
							// players and such
							return physObject;
					}
					if (groundTile.getPhysicalObject2() != null
							&& groundTile.getPhysicalObject2().getUID() != -1L) {
						RSObject physObject = new RSObject(groundTile
								.getPhysicalObject2());
						if (physObject.getType() == 2) // We don't want NPCs
							// players and such
							return physObject;
					}
					if (groundTile.getBoundaryPhysicalObject() != null
							&& groundTile.getBoundaryPhysicalObject().getUID() != -1L) {
						RSObject physObject = new RSObject(groundTile
								.getBoundaryPhysicalObject());
						if (physObject.getType() == 2) // We don't want NPCs
							// players and such
							return physObject;
					}
				}
			} catch (Exception ignored) {
			}
		}
		return null;
	}

	public RSObject getObjectAt(Tile tile) {
		return getObjectAt(tile.getX(), tile.getY());
	}

	public RSObject[] getObjects(int range) {
		List<RSObject> out = new LinkedList<RSObject>();
		Tile myPos = getMyPlayer().getLocation();
		int minX = myPos.getX() - range;
		int minY = myPos.getY() - range;
		int maxX = myPos.getX() + range;
		int maxY = myPos.getY() + range;
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				RSObject o = getObjectAt(x, y);
				if (o != null) {
					out.add(o);
				}
			}
		}
		return out.toArray(new RSObject[1]);
	}

	public RSObject[] getObjects(int range, int... ids) {
		List<RSObject> out = new LinkedList<RSObject>();
		Tile myPos = getMyPlayer().getLocation();
		int minX = myPos.getX() - range;
		int minY = myPos.getY() - range;
		int maxX = myPos.getX() + range;
		int maxY = myPos.getY() + range;
		for (int x = minX; x < maxX; x++) {
			for (int y = minY; y < maxY; y++) {
				RSObject o = getObjectAt(x, y);
				if (o != null) {
					for (int id : ids) {
						if (o.getID() == id) {
							out.add(o);
						}
					}
				}
			}
		}
		return out.toArray(new RSObject[1]);
	}

	public int getOpenedTab() {
		if (getBankInterfaceGroup().getInterface(BANK_TAB_ALL).getTextureID() == 1419)
			return BANK_TAB_ALL;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_2).getTextureID() == 1419)
			return BANK_TAB_2;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_3).getTextureID() == 1419)
			return BANK_TAB_3;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_4).getTextureID() == 1419)
			return BANK_TAB_4;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_5).getTextureID() == 1419)
			return BANK_TAB_5;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_6).getTextureID() == 1419)
			return BANK_TAB_6;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_7).getTextureID() == 1419)
			return BANK_TAB_7;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_8).getTextureID() == 1419)
			return BANK_TAB_8;
		if (getBankInterfaceGroup().getInterface(BANK_TAB_9).getTextureID() == 1419)
			return BANK_TAB_9;
		return -1;
	}

	public int getPercentageToNextLevel(int skill) {
		int level = getLevel(skill);
		if (level == -1) {
			return -1;
		}
		if (level == 99)
			return 0;

		int experience = getExperience(skill);
		int needed = expTable[level + 1];
		return (needed - experience) / (expTable[level + 1] - expTable[level])
				* 100;
	}

	public Vector<RSPlayer> getPlayers() {
		int[] validPlayers = getClient().getPlayerIndices();
		Player[] players = getClient().getPlayers();

		Vector<RSPlayer> ret = new Vector<RSPlayer>();
		for (int element : validPlayers) {
			if (players[element] == null) {
				continue;
			}
			try {

				RSPlayer Monster = new RSPlayer(players[element]);
				ret.add(Monster);

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return ret;
	}

	public int getPrayerPoints() {
		try {
			return Integer
					.parseInt(RSInterfaces.getInterface(749, 4).getText());
		} catch (final NumberFormatException e) {
			return -1;
		}
	}

	public int getRealDistanceTo(final Tile t, final boolean isObject) {
		final Tile curPos = getMyPlayer().getLocation();
		return Calc.getRealDistanceTo(curPos.getX() - getClient().getBaseX(),
				curPos.getY() - getClient().getBaseY(), t.getX()
						- getClient().getBaseX(), t.getY()
						- getClient().getBaseY(), isObject);
	}

	public int getRunEnergy() {
		try {
			return Integer
					.parseInt(RSInterfaces.getInterface(750, 5).getText());
		} catch (final NumberFormatException e) {
			return -1;
		}
	}

	public int getSetting(final int setting) {
		final int[] settings = getClient().getSettingArray();
		if (settings != null && setting < settings.length) {
			return settings[setting];
		}
		return -1;
	}

	public int getSpecial() {
		return getSetting(300);
	}

	public boolean getSpecialEnabled() {
		return (getSetting(301)) == 1;
	}

	public int getStatIndex(String statName) {
		for (int i = 0; i < statsNameArray.length; i++)
			if (statsNameArray[i].equalsIgnoreCase(statName))
				return i;
		return -1;
	}

	public RSInterface[] getTabButtons() {
		RSInterface[] out = new RSInterface[15];
		RSInterfaceGroup interfaceGroup = RSInterfaces.getInterfaceGroup(548);
		int cTab = 0;
		for (int i = 21; i < 46; i++) {
			if (i < 28 || i > 38) {
				out[cTab] = interfaceGroup.getInterface(i);
				cTab++;
			}
		}
		out[14] = interfaceGroup.getInterface(70);
		return out;
	}

	public void holdKey(char keyCode, int millis) {
		getKeyboard().holdKey(keyCode, millis);
	}

	public boolean inventoryContains(final int... itemID) {
		for (final int i : itemID) {
			if (getInventoryItemById(i) == null) {
				return false;
			}
		}
		return true;
	}

	public boolean inventoryContainsOneOf(final int... itemID) {
		RSItem[] items = getItems();
		for (final RSItem item : items) {
			for (final int i : itemID) {
				if (item.getID() == i) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean isBankOpen() {
		return getBankInterfaceGroup() != null;
	}

	public boolean isCarryingItem(final int... items) {
		return equipmentContains(items) || inventoryContains(items);
	}

	public boolean isItemClickable(RSInterface item) {
		Rectangle bounds = getBankInterfaceGroup().getInterface(BANK_ITEMPANE)
				.getArea();
		Rectangle rect = item.getArea();
		return bounds.contains(rect);
	}

	public boolean isLoggedIn() {
		Client client = getClient();
		if (client == null)
			return false;
		int gameState = client.getGameState();
		return gameState == 30 || gameState == 25;
	}

	public boolean isLoginScreen() {
		return getClient().getGameState() == 10;
	}

	public boolean isMenuOpen() {
		return getClient().isMenuOpen();
	}

	public boolean isRunning() {
		return getSetting(173) == 1;
	}

	public boolean logout() {
		clickMouse(752, 6, 12, 17, true);
		wait(random(500, 1500));
		if (RSInterfaces.getInterface(548).isValid()) {
			clickMouse(578, 366, 120, 20, true);
			wait(random(1000, 1500));
			return true;
		} else {
			return false;
		}
	}

	public void mouseChatButton(final int button, final boolean left) {
		int x = Constants.CHAT_BUTTON_CENTER_X + Constants.CHAT_BUTTON_DIFF_X
				* button;
		x = random(x - Constants.CHAT_BUTTON_MAX_DX, x
				+ Constants.CHAT_BUTTON_MAX_DX);
		int y = Constants.CHAT_BUTTON_CENTER_Y;
		y = random(y - Constants.CHAT_BUTTON_MAX_DY, y
				+ Constants.CHAT_BUTTON_MAX_DY);
		moveMouse(x, y);
		wait(random(200, 300));

		clickMouse(left);
	}

	public boolean moveMouse(int x, int y) {
		return Bot.gui.rs.mouseManager.MMouse(x, y);
	}

	public boolean moveMouse(int x, int y, int randXDeviation,
			int randYDeviation) {
		return Bot.gui.rs.mouseManager.MMouse(x, y, randXDeviation,
				randYDeviation);
	}

	public boolean moveMouse(Point p) {
		return Bot.gui.rs.mouseManager.MMouse(p.x, p.y);
	}

	public boolean moveMouse(Point p, int randXDeviation, int randYDeviation) {
		return Bot.gui.rs.mouseManager.MMouse(p.x, p.y, randXDeviation,
				randYDeviation);
	}

	public boolean onScreen(Point p) {
		return Calc.onScreen(p);
	}

	public void openClanchatTab() {
		openTab(Constants.TAB_CLANCHAT);
	}

	public void openEmoteTab() {
		openTab(Constants.TAB_EMOTES);
	}

	public void openEquipmentTab() {
		openTab(Constants.TAB_EQUIPMENT);
	}

	public void openFriendTab() {
		openTab(Constants.TAB_FRIENDS);
	}

	public void openIgnoreTab() {
		openTab(Constants.TAB_IGNORE);
	}

	public void openInventory() {
		openTab(Constants.TAB_INVENTORY);
	}

	public void openInventoryTab() {
		openTab(Constants.TAB_INVENTORY);
	}

	public void openMusicTab() {
		openTab(Constants.TAB_MUSIC);
	}

	public void openOptionsTab() {
		openTab(Constants.TAB_OPTIONS);
	}

	public void openPrayerTab() {
		openTab(Constants.TAB_PRAYER);
	}

	public void openSpellbookTab() {
		openTab(Constants.TAB_SPELLS);
	}

	public void openStatsTab() {
		openTab(Constants.TAB_STATS);
	}

	public void openSummonTab() {
		openTab(Constants.TAB_SUMMONING);
	}

	public void openTab(int tab) {
		if (tab == getCurrentTab()) {
			return;
		}
		RSInterface[] tabs = getTabButtons();
		int id = tab;
		if (id >= 7 && id != 14) {
			id -= 7;
		} else if (id != 14) {
			id += 7;
		}
		tabs[id].doClick();
	}

	public int random(double a, double b) {
		return Maths.randomBetween((int) a, (int) b);
	}

	public int random(int a, int b) {
		return Maths.randomBetween(a, b);
	}

	public Tile[] randomizePath(final Tile[] path, final int maxXDeviation,
			final int maxYDeviation) {
		final Tile[] rez = new Tile[path.length];
		for (int i = 0; i < path.length; i++) {
			rez[i] = randomizeTile(path[i], maxXDeviation, maxYDeviation);
		}
		return rez;
	}

	public Tile randomizeTile(final Tile tile, final int maxXDeviation,
			final int maxYDeviation) {
		int x = tile.getX();
		int y = tile.getY();
		if (maxXDeviation > 0) {
			double d = random.nextDouble() * 2;
			d -= 1.0;
			d *= maxXDeviation;
			x += (int) d;
		}
		if (maxYDeviation > 0) {
			double d = random.nextDouble() * 2;
			d -= 1.0;
			d *= maxYDeviation;
			y += (int) d;
		}
		return new Tile(x, y);
	}

	public Point returnMidInterface(final RSInterface child) {
		Point point = new Point(-1, -1);
		final Rectangle rect = child.getArea();
		if (rect != null) {
			point = new Point((int) rect.getCenterX(), (int) rect.getCenterY());
		}
		return point;
	}

	public void scrollToBankItem(RSInterface item) {

		RSInterface scrollbar = getBankInterfaceGroup().getInterface(
				BANK_SCROLLBAR);
		if (scrollbar == null)
			return;
		if (scrollbar.getChildren() == null)
			return;
		if (scrollbar.getChildren().length == 0)
			return;

		RSInterface arrow_up = scrollbar.getChild(BANK_SCROLLBAR_UP);
		RSInterface arrow_down = scrollbar.getChild(BANK_SCROLLBAR_DOWN);
		boolean pressing = false;
		RSInterface pressingInterface = null;
		Point pressingPoint = new Point(-1, -1);

		Rectangle bounds = getBankInterfaceGroup().getInterface(BANK_ITEMPANE)
				.getArea();
		for (int i = 0; i < 100; i++) {
			Rectangle rect = item.getArea();

			if (rect.y < bounds.y) {
				// Move up
				if (pressing) {
					if (pressingInterface != arrow_up) {
						getMouse().releaseMouse(pressingPoint.x,
								pressingPoint.y, true);
						pressingInterface = null;
						pressing = false;
					}
					wait(random(40, 70));
				} else {
					pressingInterface = arrow_up;
					pressingPoint = pressingInterface.getRandomPointInside();
					getMouse().moveMouse(pressingPoint.x, pressingPoint.y);
					wait(random(40, 70));
					getMouse().pressMouse(pressingPoint.x, pressingPoint.y,
							true);
					wait(random(40, 70));
					pressing = true;
				}
			} else if (rect.y + rect.height > bounds.y + bounds.height) {
				// Move down
				if (pressing) {
					if (pressingInterface != arrow_down) {
						getMouse().releaseMouse(pressingPoint.x,
								pressingPoint.y, true);
						pressingInterface = null;
						pressing = false;
					}
					wait(random(40, 70));
				} else {
					pressingInterface = arrow_down;
					pressingPoint = pressingInterface.getRandomPointInside();
					getMouse().moveMouse(pressingPoint.x, pressingPoint.y);
					wait(random(40, 70));
					getMouse().pressMouse(pressingPoint.x, pressingPoint.y,
							true);
					wait(random(40, 70));
					pressing = true;
				}
			} else {
				if (pressing) {
					getMouse().releaseMouse(pressingPoint.x, pressingPoint.y,
							true);
					pressing = false;
				}
				wait(random(40, 70));
				break;
			}
		}
	}

	public void sendText(String txt, boolean pressEnter) {
		getKeyboard().sendText(txt);
		if (pressEnter) {
			getKeyboard().pressEnter();
		}
	}

	public void setAltitude(boolean maxAltitude) {
		final char key = (char) (maxAltitude ? KeyEvent.VK_UP
				: KeyEvent.VK_DOWN);
		getKeyboard().pressKey(key);
		Bot.methods.wait(Bot.methods.random(1000, 1500));
		getKeyboard().releaseKey(key);
	}

	public boolean setBankRearangeMode(boolean insert) {
		if (!isBankOpen())
			return false;
		if (getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) != (insert ? 1 : 0)) {
			getBankInterfaceGroup().getInterface(BANK_BUTTON_INSERT_ITEMS_MODE)
					.doClick();
			wait(random(100, 300));
		}
		return getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) == (insert ? 1 : 0);
	}

	public boolean setBankTab(int bankTab) {
		boolean valid = false;
		if (bankTab == BANK_TAB_ALL)
			valid = true;
		else if (bankTab == BANK_TAB_2)
			valid = true;
		else if (bankTab == BANK_TAB_3)
			valid = true;
		else if (bankTab == BANK_TAB_4)
			valid = true;
		else if (bankTab == BANK_TAB_5)
			valid = true;
		else if (bankTab == BANK_TAB_6)
			valid = true;
		else if (bankTab == BANK_TAB_7)
			valid = true;
		else if (bankTab == BANK_TAB_8)
			valid = true;
		else if (bankTab == BANK_TAB_9)
			valid = true;

		if (valid) {
			getBankInterfaceGroup().getInterface(bankTab).doClick();
			return true;
		}
		return false;
	}

	public boolean setBankWithdrawingMode(boolean note) {
		if (!isBankOpen())
			return false;
		if (getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) != (note ? 1 : 0)) {
			getBankInterfaceGroup().getInterface(BANK_BUTTON_NOTE).doClick();
			wait(random(100, 300));
		}
		return getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) == (note ? 1 : 0);
	}

	public void setCameraAltitude(final boolean maxAltitude) {
		final char key = (char) (maxAltitude ? KeyEvent.VK_UP
				: KeyEvent.VK_DOWN);
		getKeyboard().pressKey(key);
		wait(random(1000, 1500));
		getKeyboard().releaseKey(key);
	}

	public void setCameraAngle(int degrees) {
		char left = KeyEvent.VK_LEFT;
		char right = KeyEvent.VK_RIGHT;
		char whichDir = left;
		int start = getCameraAngle();

		start = start < 180 ? start + 360 : start;
		degrees = degrees < 180 ? degrees + 360 : degrees;

		if (degrees > start) {
			if (degrees - 180 < start) {
				whichDir = right;
			}
		} else if (start > degrees) {
			if (start - 180 >= degrees) {
				whichDir = right;
			}
		}
		degrees %= 360;

		getKeyboard().pressKey(whichDir);
		int timeWaited = 0;
		while (getCameraAngle() > degrees + 5 || getCameraAngle() < degrees - 5) {
			Bot.methods.wait(10);
			timeWaited += 10;
			if (timeWaited > 500) {
				int time = timeWaited - 500;
				if (time == 0) {
					getKeyboard().pressKey(whichDir);
				} else if (time % 40 == 0) {
					getKeyboard().pressKey(whichDir);
				}
			}
		}
		getKeyboard().releaseKey(whichDir);
	}

	public void setCameraAngleTo(Tile tile) {
		setCameraAngle(getCameraAngleTo(tile));
	}

	public void setCompass(char direction) {
		direction = Character.toLowerCase(direction);
		switch (direction) {
		case 'n':
			setCameraAngle(359);
			break;
		case 'e':
			setCameraAngle(89);
			break;
		case 's':
			setCameraAngle(179);
			break;
		case 'w':
			setCameraAngle(269);
			break;
		}
	}

	public void setFightMode(final int fightMode) {
		if (fightMode != getFightMode()) {
			openTab(Constants.TAB_ATTACK);
			if (fightMode == 0) {
				clickMouse(577, 253, 55, 35, true);
			} else if (fightMode == 1) {
				clickMouse(661, 253, 55, 35, true);
			} else if (fightMode == 2) {
				clickMouse(576, 306, 55, 35, true);
			} else if (fightMode == 3) {
				clickMouse(662, 308, 55, 35, true);
			}
		}
	}

	public boolean setPrivateChat(final CHAT_MODE mode) {
		if (mode.equals(CHAT_MODE.HIDE)) {
			throw new IllegalArgumentException("Bad mode: HIDE");
		}
		mouseChatButton(3, false);
		return atMenu(mode.toString());
	}

	public boolean setPublicChat(final CHAT_MODE mode) {
		mouseChatButton(2, false);
		return atMenu(mode.toString());
	}

	public boolean setRun(final boolean enable) {
		if (getSetting(173) != (enable ? 1 : 0)) {
			RSInterfaces.getInterface(750, 1).doClick();
			wait(random(100, 300));
		}
		return getSetting(SETTING_BANK_TOGGLE_WITHDRAW_MODE) == (enable ? 1 : 0);

	}

	public boolean tileOnMap(final Tile t) {
		final Point p = Calc.tileToMinimap(t);
		return p != null && p.x != -1 && p.y != -1;
	}

	public boolean tileOnScreen(final Tile t) {
		final Point p = Calc.tileToScreen(t, 0);
		return p.getX() > 0 && p.getY() > 0;
	}

	public void turnOnSpecial() {
		if (getSpecial() == 1000 && !getSpecialEnabled()) {
			openTab(TAB_ATTACK);
			clickMouse(645 + random(0, 4), 425 + random(0, 4), true);
			wait(3000);
		}
	}

	/* PLAYER */

	public void turnToTile(final Tile tile) {
		setCameraAngleTo(tile);
	}

	/*
	 * UTIL MATH
	 */

	public boolean useItem(final RSItem item, final RSItem targetItem) {
		if (getCurrentTab() != Constants.TAB_INVENTORY) {
			openTab(Constants.TAB_INVENTORY);
		}
		atInventoryItem(item.getID(), "Use");
		return atInventoryItem(targetItem.getID(), "Use");
	}

	public boolean useItem(final RSItem item, final RSObject targetObject) {
		if (getCurrentTab() != Constants.TAB_INVENTORY) {
			openTab(Constants.TAB_INVENTORY);
		}
		atInventoryItem(item.getID(), "Use");
		return atObject(targetObject, "Use");
	}

	/* WAIT */
	public void wait(final int sleep) {
		try {
			final long start = System.currentTimeMillis();
			Thread.sleep(sleep);

			long current;
			long ctm = System.currentTimeMillis();
			while (start + sleep > (current = ctm)) {
				Thread.sleep(start + sleep - current);
			}
		} catch (final InterruptedException e) {
			e.printStackTrace();
		}
	}

	/* RANDOM */

	public boolean walkTile(final Tile t) {
		final Point p = Calc.tileToMinimap(t);
		if (p.x == -1 || p.y == -1) {
			return false;
		}
		clickMouse(p, 2, 2, true);
		return true;
	}

	public boolean withdrawBankItem(int itemID, int count) {
		if (!isBankOpen()) {
			Bot.addMessage(this,
					"Bank is not open. Can't withdraw item with ID: " + itemID
							+ ".");
			return false;
		}
		if (!bankContains(itemID)) {
			Bot.addMessage(this, "Bank don't contain item with ID: " + itemID
					+ ".");
		}
		if (getOpenedTab() != BANK_TAB_ALL) {
			setBankTab(BANK_TAB_ALL);
		}
		RSInterface item = getBankItem(itemID);
		if (item == null) {
			return false;
		}
		scrollToBankItem(item);
		if (!isItemClickable(item)) {
			return false;
		}

		if (count < -1) {
			throw new IllegalArgumentException("Can't withdraw less than 1");
		}

		if (count == MENU_ALL) {
			return item.doAction("Withdraw-All");
		} else if (count == MENU_ALL_BUT_ONE) {
			return item.doAction("Withdraw-All but one");
		} else {
			if (item.doAction("Withdraw-" + count)) {
				return true;
			} else {
				item.doAction("Withdraw-X");
				for (int i = 0; i < 21; i++) {
					if (RSInterfaces.interfaceGroupExists(752)) {
						sendText("" + count, true);
						return true;
					}
					wait(random(80, 130));
				}
				return false;
			}
		}
	}

	public boolean withdrawBankItem(String itemName, int count) {
		if (!isBankOpen()) {
			Bot.addMessage(this, "Bank is not open. Can't withdraw item: "
					+ itemName + ".");
			return false;
		}
		if (!bankContains(itemName)) {
			Bot.addMessage(this, "Bank don't contain item: " + itemName + ".");
		}

		if (getOpenedTab() != BANK_TAB_ALL) {
			setBankTab(BANK_TAB_ALL);
		}

		RSInterface item = getBankItem(itemName);
		if (item == null) {
			return false;
		}
		scrollToBankItem(item);
		if (!isItemClickable(item)) {
			return false;
		}

		if (count < -1) {
			throw new IllegalArgumentException("Can't withdraw less than 1");
		}

		if (count == MENU_ALL) {
			return item.doAction("Withdraw-All");
		} else if (count == MENU_ALL_BUT_ONE) {
			return item.doAction("Withdraw-All but one");
		} else {
			if (item.doAction("Withdraw-" + count)) {
				return true;
			} else {
				item.doAction("Withdraw-X");
				for (int i = 0; i < 10; i++) {
					wait(random(300, 600));
					if (RSInterfaces.interfaceGroupExists(752)) {
						sendText("" + count, true);
						return true;
					}

				}
				return false;
			}
		}

	}

}
